home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Format CD 31
/
Amiga Format CD31 (1998-09-02)(Future Publishing)(GB)(Track 1 of 2)[!][issue 1998-10].iso
/
-seriously_amiga-
/
hardware
/
transadf
/
source
/
pkzip.c
< prev
next >
Wrap
C/C++ Source or Header
|
1998-07-20
|
19KB
|
559 lines
/* pkzip.c - Handle all PKZip-file specific tasks
** Copyright (C) 1997,1998 Karl J. Ots
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*---------------------*/
/* PKZip file routines */
/*---------------------*/
#include <exec/types.h>
#include <dos/dos.h>
#include <clib/dos_protos.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "pkzip.h"
#include "main.h"
#include "util.h"
#include "errors.h"
/* Private functions */
LONG GotoFirstField (BPTR file, ULONG FieldID);
/*----------------------------------*/
/* Constants, structures and macros */
/*----------------------------------*/
extern const char ZipComment[]; /* Defined in version.c */
/* PKZip Local Header structure */
struct PKZHead {
ULONG MagicNum; /* Magic Number, = 0x504B0304 ('P','K',3,4). */
UBYTE MinVer; /* Min. required UnZip version (20 for defl). */
UBYTE MinOS; /* Min. requires OS, MS-DOS = 0. */
UWORD Flags; /* Flags. */
UWORD CMethod; /* Compression method, deflate = 8. */
ULONG Date; /* Last moddification date. */
ULONG CRC; /* Uncompressed Data CRC. */
ULONG CSize; /* Compressed data length. */
ULONG USize; /* Uncompressed data length. */
UWORD FNameLen; /* File name length. */
UWORD EFieldLen; /* Extra field length. */
}; /* sizeof = 30 */
/* PKZip Central Record structure */
struct PKZCenRec {
ULONG MagicNum; /* Magic Number, = 0x504B0102 ('P','K',1,2). */
UBYTE ZipVer; /* Zip version number x10 (ie 20 = v2.0). */
UBYTE HostOS; /* Host OS, Amiga = 1. */
UBYTE MinVer; /* Min. required UnZip version (20 for defl). */
UBYTE MinOS; /* Min. requires OS, MS-DOS = 0. */
UWORD Flags; /* Flags. */
UWORD CMethod; /* Compression method, deflate = 8. */
ULONG Date; /* Last moddification date. */
ULONG CRC; /* Uncompressed Data CRC. */
ULONG CSize; /* Compressed data length. */
ULONG USize; /* Uncompressed data length. */
UWORD FNameLen; /* File name length. */
UWORD EFieldLen; /* Extra field length. */
UWORD FCommentLen; /* File comment length. */
UWORD DiskNum; /* Disk number in multi-part zip. */
UWORD FileType; /* File Type, set bit 0 for text. */
UWORD MSDAttrib; /* MS-DOS File Attributes. */
UWORD Attrib; /* File Attributes, Amiga rw-d = 0x0D04 (LE). */
ULONG LHeadOff; /* Offset of local header. */
}; /* sizeof = 46 */
struct PKZEndCRec {
ULONG MagicNum; /* Magic Number, = 0x504B0506 ('P','K',5,6). */
UWORD DiskNum; /* Number of this disk in multi-disk zip. */
UWORD CentRecDisk; /* Disk number with start of Central Record. */
UWORD EntriesOnDisk; /* Number of entries on this disk. */
UWORD CenRecEntries; /* Total number of Central Record Entries. */
ULONG CenRecSize; /* Total Size of Central Record. */
ULONG CenRecOffset; /* Offset of start of Central Record. */
UWORD CommentLen; /* Length of Zip Comment. */
}; /* sizeof = 22 */
#define LHD_MAGNUM 0x504B0304 /* Local Header Magic Number */
#define CRC_MAGNUM 0x504B0102 /* Central Record Magic Number */
#define ECR_MAGNUM 0x504B0506 /* End of Central Record Magic Number */
/* Used to store the date/time that compression started */
ULONG startDate;
/* Used to store the location of the `current' PKZip header */
LONG pkzHeadOffset;
STRPTR pkzOrigName;
/* Used to save the central record of archive when adding */
ULONG Adding; /* Set to 0x12345678 if adding, cleared afterwards */
ULONG pkzOldCenRecSize;
APTR pkzOldCenRec;
/* End of central record - one per zip archive */
struct PKZEndCRec pkzECRec;
/*
** Output a PKZip Header to the specified file.
** Return TRUE if no errors. else FALSE.
*/
BOOL writePKZHead (BPTR outFile, STRPTR origName)
{
UBYTE Empty[30];
UWORD nlen;
/* Simply write an empty sizeof (struct PKZHead) + strlen (origName). */
/* We'll fill in the details later. */
/* Note the date and time */
startDate = dosDate();
/* Make sure we have a name */
if (!origName) origName = "disk.adf";
/* Save the current position and name */
pkzHeadOffset = Seek (outFile, 0, OFFSET_CURRENT);
pkzOrigName = strdup (origName);
/* Write the `header' and the name to the file */
if ( Write (outFile, Empty, 30) != 30) return FALSE;
/* Get the filename length and write the filename */
nlen = strlen (origName);
if ( Write (outFile, origName, nlen) != nlen) return FALSE;
/* PKZip file is now ready to recieve the compressed data */
return TRUE;
}
/*
** Finish writing a PKZip file.
** Return TRUE if no errors. else FALSE.
*/
BOOL finishPKZFile (BPTR outFile, ULONG CRC, ULONG CSize, ULONG USize)
{
LONG CRecPos;
ULONG Date;
UWORD FNameLen, ZCommentLen;
/* Local header - one per zipped file */
struct PKZHead pkzHead = {LHD_MAGNUM, /* PKZip Magic Number */
20, /* Min. UnZip version (2.0) */
0, /* Min. OS (MS-DOS) */
0, /* Flags */
0x0800, /* Compression Method (LE) */
0, /* Date (to be filled in) */
0, /* CRC (to be filled in) */
0, /* CSize (to be filled in) */
0, /* USize (to be filled in) */
0, /* Filename length (tbfi) */
0}; /* Extra field length */
/* Central record entry - one per zipped file */
struct PKZCenRec pkzCenRec = {CRC_MAGNUM, /* Central Rec Magic Number */
20, /* Zip version (2.0) */
1, /* Host OS (Amiga) */
20, /* Min. UnZip version (2.0) */
0, /* Min. Host OS (MS-DOS) */
0, /* Flags */
0x0800, /* Compression Mode (LE) */
0, /* Date (to be filled in) */
0, /* CRC (to be filled in) */
0, /* CSize (to be filled in) */
0, /* USize (to be filled in) */
0, /* File Name Length (tbfi) */
0, /* Extra field length */
0, /* File comment length */
0, /* Disk Number */
0, /* File Type (binary) */
0, /* MS-DOS file attributes */
0x0D04, /* File Attributes (rw-d) */
0}; /* Local header offset (tbfi) */
/* Seek to the local header and save the `current' position */
CRecPos = Seek (outFile, pkzHeadOffset, OFFSET_BEGINNING);
/* 'pre-process' CRC, CSize and USize */
CRC = LEL (CRC);
CSize = LEL (CSize);
USize = LEL (USize);
Date = LEL (startDate);
FNameLen = strlen (pkzOrigName);
ZCommentLen = strlen (ZipComment);
/* Fill the structures */
pkzHead.Date = Date;
pkzHead.CRC = CRC;
pkzHead.CSize = CSize;
pkzHead.USize = USize;
pkzHead.FNameLen = LES (FNameLen);
pkzCenRec.Date = Date;
pkzCenRec.CRC = CRC;
pkzCenRec.CSize = CSize;
pkzCenRec.USize = USize;
pkzCenRec.FNameLen = LES (FNameLen);
pkzCenRec.LHeadOff = LEL (pkzHeadOffset);
/* Fill the End of Central Record structure */
pkzECRec.MagicNum = ECR_MAGNUM;
pkzECRec.DiskNum = 0;
pkzECRec.CentRecDisk = 0;
pkzECRec.CenRecOffset = LEL (CRecPos);
pkzECRec.CommentLen = LES (ZCommentLen);
if (Adding == 0x12345678)
{
pkzECRec.EntriesOnDisk = LES ( LES (pkzECRec.EntriesOnDisk) + 1);
pkzECRec.CenRecEntries = LES ( LES (pkzECRec.CenRecEntries) + 1);
pkzECRec.CenRecSize = LEL (pkzOldCenRecSize + 46 + FNameLen);
}
else
{
pkzECRec.EntriesOnDisk = LES (1);
pkzECRec.CenRecEntries = LES (1);
pkzECRec.CenRecSize = LEL (46 + FNameLen);
}
/* Write the Local Header and seek to the Central Record offset */
if ( Write (outFile, &pkzHead, 30) != 30) return FALSE;
/* Write (outFile, pkzOrigName, FNameLen); */ /* Already done */
Seek (outFile, CRecPos, OFFSET_BEGINNING);
if (Adding == 0x12345678)
{
/* Write the old central record before adding the new one */
if ( Write (outFile, pkzOldCenRec, pkzOldCenRecSize) != pkzOldCenRecSize)
return FALSE;
}
/* Write the Central Record and origName */
if ( Write (outFile, &pkzCenRec, 46) != 46) return FALSE;
if ( Write (outFile, pkzOrigName, FNameLen) != FNameLen) return FALSE;
/* Write the End of Central Record and File Comment */
if ( Write (outFile, &pkzECRec, 22) != 22) return FALSE;
if ( Write (outFile, ZipComment, ZCommentLen) != ZCommentLen) return FALSE;
/* Don't need the name anymore */
free (pkzOrigName);
/* Don't need the old record anymore */
if (Adding == 0x123456780) free (pkzOldCenRec);
/* No longer adding */
Adding = 0;
/* Done! */
return TRUE;
}
/*
** Output a PKZip Header to the specified file, adding
** it to an existing archive.
** Return TRUE if no errors. else FALSE.
*/
BOOL writePKZHeadAdd (BPTR outFile, STRPTR origName)
{
LONG DOSError;
/* We're adding */
Adding = 0x12345678;
FPuts (StdOut, "Updating archive.\n");
/* Get the End of Central Record */
if ( GotoFirstField (outFile, ECR_MAGNUM) == -1)
{
DOSError = IoErr();
if (DOSError)
{
FPrintf (StdErr, "%s: Error reading file - ", ProgName);
reportDOSError(DOSError);
}
else
FPrintf (StdErr, "%s: Can't add ADF, Not a Zip file.\n", ProgName);
cleanExit (RETURN_ERROR, NULL);
}
if ( Read (outFile, &pkzECRec, 22) != 22) return FALSE;
/* We'll be adding this header at the current position of the Cent Rec */
pkzHeadOffset = LEL (pkzECRec.CenRecOffset);
/* Save the current central record for re-writing later */
pkzOldCenRecSize = LEL (pkzECRec.CenRecSize);
pkzOldCenRec = calloc (pkzOldCenRecSize, 1);
if (!pkzOldCenRec)
{
/* Memory error */
return FALSE;
}
Seek (outFile, pkzHeadOffset, OFFSET_BEGINNING);
if ( Read (outFile, pkzOldCenRec, pkzOldCenRecSize) != pkzOldCenRecSize)
return FALSE;
/* Write our dummy header and get outa here */
Seek (outFile, pkzHeadOffset, OFFSET_BEGINNING);
return writePKZHead (outFile, origName);
}
/*
** Finish writing a PKZip file, adding new a new record.
** Return TRUE if no errors. else FALSE.
*/
BOOL finishPKZFileAdd (BPTR outFile, ULONG CRC, ULONG CSize, ULONG USize)
{
return finishPKZFile (outFile, CRC, CSize, USize);
}
/*
** Skip the PKZip Header of a specified file.
** If origName is specified, search for that name within file.
** Return TRUE if no errors. else FALSE.
*/
BOOL skipPKZHead (BPTR inFile, STRPTR origName)
{
UBYTE FName[128]; /* This should be large enough */
struct PKZHead pkzHead;
struct PKZCenRec pkzCenRec;
ULONG origNameLen, pOrigNameLen;
LONG DOSError;
STRPTR pOrigName;
UWORD nlen, elen, clen;
int i;
if (origName)
{
/* Get the first central record entry */
if ( GotoFirstField (inFile, CRC_MAGNUM) == -1)
{
DOSError = IoErr();
if (DOSError)
{
FPrintf (StdErr, "%s: Error reading file - ", ProgName);
reportDOSError(DOSError);
}
else
FPrintf (StdErr, "%s: Error - Not a Zip file.\n", ProgName);
cleanExit (RETURN_ERROR, NULL);
}
/* Parse the pattern */
origNameLen = strlen (origName);
for (i=0; i < origNameLen; i++)
origName[i] = toupper (origName[i]);
pOrigNameLen = (2 * origNameLen) + 2;
pOrigName = calloc (pOrigNameLen, 1);
if (!pOrigName)
{
FPrintf (StdErr, "%s: Error - Not enough memory to match filename.\n",
ProgName);
cleanExit (RETURN_FAIL, ERROR_NO_FREE_STORE);
}
if (ParsePatternNoCase (origName, pOrigName, pOrigNameLen) == -1)
{
FPrintf (StdErr, "%s: Error - Can't match filename.\n", ProgName);
cleanExit (RETURN_FAIL, NULL);
}
/* Now go through all the names and search for a match */
while (1)
{
/* Read the entry */
if ( Read (inFile, &pkzCenRec, 46) != 46) return FALSE;
/* Test the current magic number */
if (pkzCenRec.MagicNum != CRC_MAGNUM)
{
/* We went through the whole list without a match */
FPrintf (StdErr, "%s: No match for filename.\n", ProgName);
cleanExit (RETURN_WARN, NULL);
}
/* Get extra info lengths */
nlen = LES (pkzCenRec.FNameLen);
elen = LES (pkzCenRec.EFieldLen);
clen = LES (pkzCenRec.FCommentLen);
/* Read in the name */
if ( Read (inFile, FName, nlen) != nlen) return FALSE;
FName[nlen] = 0;
/* Attempt to match the pattern */
if (MatchPatternNoCase (pOrigName, FName))
{
/* Output information */
FPrintf (StdOut, "Extracting %s.\n", FName);
/* Got a name, seek to the start of this item */
Seek (inFile, LEL (pkzCenRec.LHeadOff), OFFSET_BEGINNING);
/* Break the loop */
break;
}
/* No match, move along */
Seek (inFile, elen + clen, OFFSET_CURRENT);
}
free (pOrigName);
}
/* Save the current position */
pkzHeadOffset = Seek (inFile, 0, OFFSET_CURRENT);
/* Consume the header and extra info */
if ( Read (inFile, &pkzHead, 30) != 30) return FALSE;
if (pkzHead.MagicNum != LHD_MAGNUM)
{
FPrintf (StdErr,
"%s: Error - Didn't read a Local header - not a Zip file.\n",
ProgName);
cleanExit (RETURN_ERROR, NULL);
}
nlen = LES (pkzHead.FNameLen);
elen = LES (pkzHead.EFieldLen);
Seek (inFile, (nlen + elen), OFFSET_CURRENT);
/* Check the compression */
if ( LES (pkzHead.CMethod) != 8)
{
FPrintf (StdErr,
"%s: Error - Only Deflate compression method supported.\n",
ProgName);
cleanExit (RETURN_ERROR, NULL);
}
/* inFile now points to the compressed data */
return TRUE;
}
/*
** Read the header of a PKZip file and return the CRC
** and USize in supplied arrays.
** Return TRUE if no errors, else FALSE.
*/
BOOL readPKZTail (BPTR inFile, ULONG *CRC, ULONG *USize)
{
struct PKZHead pkzHead;
LONG oldp;
/* Get the `current' header */
oldp = Seek (inFile, pkzHeadOffset, OFFSET_BEGINNING);
if ( Read (inFile, &pkzHead, 30) != 30) return FALSE;
Seek (inFile, oldp, OFFSET_BEGINNING);
/* Fill the return values */
*CRC = LEL (pkzHead.CRC);
*USize = LEL (pkzHead.USize);
return TRUE;
}
/*
** Jump to the start of the first field with FieldID.
** Return the offset as well as setting the file pointer.
** Return -1 for error.
*/
LONG GotoFirstField (BPTR file, ULONG FieldID)
{
UBYTE Buffer[46];
/* Convenient `gateways' */
struct PKZHead *pkzHead = (struct PKZHead *) Buffer;
struct PKZCenRec *pkzCenRec = (struct PKZCenRec *) Buffer;
ULONG item, nextSeek;
int cont = TRUE;
do
{
/* Read in an item */
if ( Read (file, &item, 4) != 4) return -1;
/* Identify item and get the rest of the field if required */
switch (item) {
case LHD_MAGNUM:
/* Local header */
/* Check if want to stop here */
if (FieldID == LHD_MAGNUM)
cont = FALSE;
else
{
/* Move along */
if ( Read (file, Buffer+4, 26) != 26) return -1; /* 26 = 30 - 4 */
nextSeek = LES (pkzHead->FNameLen) + LES (pkzHead->EFieldLen) +
LEL (pkzHead->CSize);
Seek (file, nextSeek, OFFSET_CURRENT);
}
break;
case CRC_MAGNUM:
/* Central record entry */
/* Check if want to stop here */
if (FieldID == CRC_MAGNUM)
cont = FALSE;
else
{
/* Move along */
if ( Read (file, Buffer+4, 42) != 42) return -1; /* 42 = 46 - 4 */
nextSeek = LES (pkzCenRec->FNameLen) + LES (pkzCenRec->EFieldLen) +
LES (pkzCenRec->FCommentLen);
Seek (file, nextSeek, OFFSET_CURRENT);
}
break;
case ECR_MAGNUM:
/* End of central record */
/* Final destination - stop here! */
cont = FALSE;
break;
default:
/* Can't id the field - error */
return -1;
}
} while (cont);
/* Seek back to the start of the current item */
Seek (file, -4, OFFSET_CURRENT);
/* return the current offset */
return Seek (file, 0, OFFSET_CURRENT);
}